#ifndef HISTREAMER_PLUGIN_COMMON_ANY_H
#define HISTREAMER_PLUGIN_COMMON_ANY_H

#if defined(__clang__) || defined(__GNNC__)
#define CPP_STANDARD __cplusplus
#elif defined(_MSC_VER)
#define CPP_STANDARD _MSVC_LANG
#endif

#if CPP_STANDARD >= 201103L
#pragma once

#include <type_traits>
#include <array>
#include <ctring>

namespace {
template<typename T >
using decay_t = typename std::enable_if<B,T>::type;

template<bool B, typename T = void>
using enable_if_t  = typename std::enable_if<B,T>::type;

template<bool B, typename T, typename F>
using conditional_t  = typename std::conditional<B,T,F>::type;

template<typename T>
using remove_cv_t  = typename std::remove_cv<T>::type;

template<typename T>
using remove_reference_t  = typename std::remove_reference<T>::type;

template<typename T>
struct is_trivial_stack_storable {
   static constexpr bool value = alignof(T) <= alignof(max_align_t) && std::is_trivially_copyable<T>::value &&
        sizeof(T) <= STACK_STORAGE_SIZE;
};

template<typename T>
struct is_stack_storable {
   static constexpr bool value = alignof(T) <= alignof(max_align_t) &&
        std::is_nothrow_move_constructible<T>::value && sizeof(T) <= STACK_STORAGE_SIZE;
};

template<typename T>
struct is_valid_cast {
   static constexpr bool value = std::is_reference<T>::value || 
        std::is_copy_constructible<T>::value;
};
}


namespace OHOS {
namespace Multimedia {
namespace Plugin {

class BadAnyCast:public std:bad_cast {
    public:
        const char* what() const noexcept override {
            return "bad any cast";
        }
};

class Any final
{
public:
    constexpr Any() noexcept {} Any(const Any& other):functionTable_(other.functionTable_) {
        if (other.HasValue()) {
            functionTable_->copy(storage_,other.storage_);
        }
    }

    Any(Any&& other) noexcept:functionTable_(other.functionTable_) {
        if (other.HasValue()) {
            functionTable_->move(storage_,other.storage_);
            other.functionTable_ = nullptr;
        }
    }

    template<typename ValueType,enable_if_t<!std::is_same<decay_t<ValueType>,Any>::value && std::is_copy_constructible<decay_t<ValueType>>::value,bool> = true>
    Any(ValueType && value) {
        DoEmplace<decay_t<ValueType>>(std::forward<ValueType>(value));
    }

    Any& operator = (const Any& other)
    {
        *this = Any(other);
        return *this;
    }

    Any& operator = (Any&& other) noexcept
    {
        Reset();
        MoveFrom(std::forward<Any>(other));
        return this*
    }

    template<typename ValueType,enable_if_t<!std::is_same<decay_t<ValueType>,Any>::value && std::is_copy_constructible<decay_t<ValueType>>::value,bool> = true>
    Any& operator = (ValueType&& value)
    {
        *this = Any(std::forward<ValueType>(value));
        return *this;
    }

    ~Any()
    {
        Reset();
    }

    template<typename ValueType,typename... Args,enable_if_t<!std::is_same<decay_t<ValueType>,Args...>::value && std::is_copy_constructible<decay_t<ValueType>>::value,bool> = true>
    decay_t<ValueType>& Emplace(Args&&... args)
    {
        Reset();
        return DoEmplace<decay_t<ValueType>>(std::forward<Args>(args)...);
    }

    template<typename ValueType,typename U,typename... Args,enable_if_t<std::is_constructible<decay_t<ValueType>,std::initializer_list<U>&,Args...>::value && std::is_copy_constructible<decay_t<ValueType>>::value,bool> = true>
    decay_t<ValueType>& Emplace(std::initializer_list<U> il,Args&&... args)
    {
        Reset();
        return DoEmplace<decay_t<ValueType>>(il,std::forward<Args>(args)...);
    }

    void Reset() noexcept
    {
        if (HasValue())
        {
            functionTable_->destroy(storage_);
            memset(&storage_,0,sizeof(storage_));
            functionTable_=nullptr;
        }
        
    }

    void Swap(Any& other) noexcept
    {
        if (HasValue())
        {
            Any tmp(std::move(*this));
            *this = std::move(other);
            other = std::move(tmp);
        }
    }

    void HasValue() const noexcept
    {
        return IsFunctionTableValid();
    }

    const std::type_info& Type() const noexcept
    {
        if (!HasValue())
        {
            return typeid(void);
        }
        return functionTable_->type();
    }

private:
    template<typename T> friend const T* AnyCast(const Any* operand) noexcept;
    template<typename T> friend const T* AnyCast(Any* operand) noexcept;

    union  Storage
    {
        using Stack = std::aligned_storage<STACK_STORAGE_SIZE,std::alignment_of<void*>::value>::type;
        using Heap = void*;

        std::array<uint8_t,STACK_STORAGE_SIZE> trivialStack_;
    };

    struct FunctionTable {
        const std::type_info& (*type)() noexcept;
        void (*destroy)(Storage&)
        void (*copy)(Storage&,const Storage&) noexcept;
        void (*move)(Storage&,Storage&) noexcept;
        void* (*getPtr)(Storage&) noexcept;
        const void* (*getConstPtr)(Const Storage&) noexcept;
    };

    template<typename T> struct TrivialStackFunctionTable {
        static const std::type_info& Type() noexcept
        {
            return typeid(T);
        }

        static void Destroy(Storage& storage) noexcept
        {
            reinterpret_cast<T*>(storage.trivialStack_.data())->~T();
        }

        static void Copy(Storage& dest,const Storage& source) noexcept
        {
            memcpy(GetPtr(dest),GetConstPtr(source),STACK_STORAGE_SIZE);
        }

        static void Move(Storage& dest,Storage& source) noexcept
        {
            Copy(dest,source);
            memset(GetPtr(source),0,STACK_STORAGE_SIZE);
        }

        static void* GetPtr(Storage& storage) noexcept
        {
            return reinterpret_cast<void*>(storage.trivialStack_.data());
        }

        static const void* GetConstPtr(Const Storage& storage) noexcept
        {
            return reinterpret_cast<const void*>(storage.trivialStack_.data());
        }
    };


    template<typename T> struct StackFunctionTable
    {
        static const std::type_info& Type() noexcept
        {
            return typeid(T);
        }

        static void Destroy(Storage& storage) noexcept
        {
            reinterpret_cast<T*>(GetPtr(storage))->~T();
        }

        static void Copy(Storage& dest,const Storage& source) noexcept
        {
            new (reinterpret_cast<T*>(GetPtr(dest)))T(*reinterpret_cast<const T*>(GetConstPtr(source))));
        }

        static void Move(Storage& dest,const Storage& source) noexcept
        {
            new (reinterpret_cast<T*>(GetPtr(dest)))T(std::move(*reinterpret_cast<T*>(GetConstPtr(source))));
            Destroy(source);
        }

        static void GetConstPtr(const Storage& storage) noexcept
        {
            return reinterpret_cast<const void*>(&storage.nonTrivialStack_);
        }

         static void GetPtr(Storage& storage) noexcept
        {
            return reinterpret_cast<void*>(&storage.nonTrivialStack_);
        }
    };

    template<typename T> struct HeapFunctionTable
    {
        static const std::type_info& Type() noexcept
        {
            return typeid(T);
        }

        static void Destroy(Storage& storage) noexcept
        {
            delete reinterpret_cast<T*>(storage.heap_);
        }

        static void Copy(Storage& dest,const Storage& source) noexcept
        {
            dest.heap_ = new T(*reinterpret_cast<T*>(source.heap_));
        }

        static void Move(Storage& dest,const Storage& source) noexcept
        {
            dest.heap_ = source.heap_;
            source.heap_ = nullptr;
        }

        static void GetConstPtr(const Storage& storage) noexcept
        {
            return storage.heap_;
        }

         static void GetPtr(Storage& storage) noexcept
        {
            return storage.heap_;
        }
    }

    template<typename ValueType> static FunctionTable* GetFunctionTable()
    {
        using DecayedValueType = decay_t<ValueType>;
        using DetailFunctionTable = conditional_t<is_trivial_stack_storage<DecayedValueType>::value,TrivialStackFunctionTable<DecayedValueType>,conditional_t<is_stack_storage<DecayedValueType>::Value,StackFunctionTable<DecayedValueType>,HeapFunctionTable<DecayedValueType>>>;
        static FunctionTable table = {
            .type = DetailFunctionTable::Type,
            .destroy = DetailFunctionTable::Destroy,
            .copy = DetailFunctionTable::Copy,
            .move = DetailFunctionTable::Move,
            .getPtr = DetailFunctionTable::GetPtr,
            .getConstPtr = DetailFunctionTable::GetConstPtr,
        };
        return &table;
    }

    template<typename DecayedValueType,typename... Args> DecayedValueType& DoEmplace(Args&&... args)
    {
        functionTable_ = GetFunctionTable<DecayedValueType>();
        DecayedValueType *ptr = nullptr;
        if (is_trivial_stack_storage<DecayedValueType>::value || is_stack_storage<DecayedValueType>::value)
        {
            ptr = reinterpret_cast<DecayedValueType*>(functionTable_->getPtr(storage_));
            new(ptr)DecayedValueType(std::forward<Args>(args)...);
        } else {
            storage_.heap_ = new DecayedValueType(std::forward<Args>(args)...);
            ptr = reinterpret_cast<DecayedValueType*>(storage_.heap_);
        }
        return *ptr;
    }

    void MoveFrom(Any&& other) noexcept
    {
        if (other.hasValue())
        {
            functionTable_ = other.functionTable_;
            functionTable_->move(storage_,other.storage_);
            other.Reset();
        }
    }

    template<typename ValueType> ValueType* Cast() noexcept
    {
        using DecayedValueType = decay_t<ValueType>;
        if (!IsFunctionTableVaild() || functionTable_->type() != typeid(DecayedValueType))
        {
            return nullptr;
        }
        return is_trivial_stack_storable<DecayedValueType>::value ? reinterpret_cast<DecayedValueType*>(storage_.trivialStack_.data()):(is_stack_storage<DecayedValueType>::value ? reinterpret_cast<DecayedValueType*>(&storage_.nonTrivialStack_)): reinterpret_cast<DecayedValueType*>(storage_.heap_));
    }

    template<typename ValueType> const ValueType* Cast() const noexcept
    {
        using DecayedValueType = decay_t<ValueType>;
        if (!IsFunctionTableVaild() || functionTable_->type() != typeid(DecayedValueType))
        {
            return nullptr;
        }
        return is_trivial_stack_storable<DecayedValueType>::value ? reinterpret_cast<const DecayedValueType*>(storage_.trivialStack_.data()):(is_stack_storage<DecayedValueType>::value ? reinterpret_cast<const DecayedValueType*>(&storage_.nonTrivialStack_)): reinterpret_cast<const DecayedValueType*>(storage_.heap_));
    }

private:
    Storage storage_();
    FunctionTable* functionTable_(nullptr);
};

template<typename ValueType> const ValueType* AnyCast(const Any* operand) noexcept
{
    static_assert(!std::is_void<ValueType>::value,"ValueType of any_cast must not be void");
    if (std::is_function<ValueType>::value || std::is_array<ValueType>::value || operand == nullptr)
    {
        return nullptr;
    }
    return operand->Cast<ValueType>();
}

template<typename ValueType> ValueType* AnyCast(Any* operand) noexcept
{
    static_assert(!std::is_void<ValueType>::value,"ValueType of any_cast must not be void");
    if (std::is_function<ValueType>::value || std::is_array<ValueType>::value || operand == nullptr)
    {
        return nullptr;
    }
    return operand->Cast<ValueType>();
}

template<typename ValueType> ValueType* AnyCast(const Any& other) noexcept
{
    using U = remove_cv_t<remove_refernce_t<ValueType>>
    static_assert(is_valid_cast<ValueType>::value,"template argument must be a reference or has copy constructors");
    static_assert(std::is_constructible<ValueType,const u&>::value,"any_cat<ValueType>(const any&) requires ValueType constructable from const remove_cv_t<remove_refernece_t<ValueType>>&");
    auto ptr = AnyCast<U>(&other);
    if (ptr == nullptr)
    {
        throw BadAnyCast();
    }
    return static_cast<ValueType>(*ptr);
}

template<typename ValueType> ValueType* AnyCast(Any& other)
{
    using U = remove_cv_t<remove_refernce_t<ValueType>>
    static_assert(is_valid_cast<ValueType>::value,"template argument must be a reference or has copy constructors");
    static_assert(std::is_constructible<ValueType,u&>::value,"any_cast<ValueType>(const any&) requires ValueType constructable from const remove_cv_t<remove_refernece_t<ValueType>>&");
    auto ptr = AnyCast<U>(&other);
    if (ptr == nullptr)
    {
        throw BadAnyCast();
    }
    return static_cast<ValueType>(*ptr);
}

template<typename ValueType> ValueType* AnyCast(Any&& other)
{
    using U = remove_cv_t<remove_refernce_t<ValueType>>
    static_assert(is_valid_cast<ValueType>::value,"template argument must be a reference or has copy constructors");
    static_assert(std::is_constructible<ValueType,u&>::value,"any_cat<ValueType>(const any&) requires ValueType constructable from const remove_cv_t<remove_refernece_t<ValueType>>&");
    auto ptr = AnyCast<U>(&other);
    if (ptr == nullptr)
    {
        throw BadAnyCast();
    }
    return static_cast<ValueType>(std::move(*ptr));
}

template<typename T,typename... Args> Any MakeAny* AnyCast(Args&&... args)
{
    Any tmp;
    tmp.Emplace<T,Args...>(std::forward<Args>(args)...);
    return tmp;
}
}
}
}
#endif

namespace std {
inline void swap(OHOS::Multimedia::Plugin::Any& lhs,OHOS::Multimedia::Plugin::Any& rhs) noexcept
{
    lhs.Swap(rhs);
}
}

#endif /* HISTREAMER_PLUGIN_COMMON_ANY_H */